package com.qire.manhua.view.textbox;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Typeface;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;

import com.qire.manhua.R;

import net.yslibrary.android.keyboardvisibilityevent.util.UIUtil;

import androidx.annotation.Nullable;
import androidx.databinding.BindingAdapter;
import androidx.databinding.InverseBindingListener;
import androidx.databinding.InverseBindingMethod;
import androidx.databinding.InverseBindingMethods;

public class TextBoxGroupView extends LinearLayout {

    private int boxCount;
    private int boxWidth, boxHeight, boxSpacing;
    private float boxTextSize;
    private int boxBackground;
    private OnCodeChangeListener onCodeChangeListener;

    private boolean disableSelect;
    private int lastFocusIndex = 0;

    public TextBoxGroupView(Context context) {
        this(context, null);
    }

    public TextBoxGroupView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public TextBoxGroupView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public TextBoxGroupView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs, defStyleAttr);
    }

    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
        setOrientation(HORIZONTAL);

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TextBoxGroupView, defStyleAttr, 0);
        this.boxCount = typedArray.getInt(R.styleable.TextBoxGroupView_boxCount, 0);
        this.boxWidth = typedArray.getDimensionPixelSize(R.styleable.TextBoxGroupView_boxWidth, -1);
        this.boxHeight = typedArray.getDimensionPixelSize(R.styleable.TextBoxGroupView_boxHeight, -1);
        this.boxSpacing = typedArray.getDimensionPixelSize(R.styleable.TextBoxGroupView_boxSpacing, -1);
        this.boxTextSize = typedArray.getDimensionPixelSize(R.styleable.TextBoxGroupView_boxTextSize, 15);
        this.boxBackground = typedArray.getResourceId(R.styleable.TextBoxGroupView_boxBackground, 0);
        this.disableSelect = typedArray.getBoolean(R.styleable.TextBoxGroupView_disableSelect, true);
        typedArray.recycle();

        updateBox();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if(disableSelect) {
            setFocusIndex(lastFocusIndex);
            return true;
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        if(child instanceof TailInputEditText) {
            TailInputEditText boxView = (TailInputEditText) child;
            BoxTextTextWatcher textTextWatcher = new BoxTextTextWatcher(getChildCount());
            boxView.addTextChangedListener(textTextWatcher);
            boxView.setOnKeyListener(textTextWatcher);
            super.addView(child, index, params);
        }
    }

    private TailInputEditText getBoxView(int index) {
        View view = getChildAt(index);
        if(view instanceof TailInputEditText) {
            return (TailInputEditText) view;
        }
        return null;
    }

    private void updateBoxView(TailInputEditText boxView, int index) {
        ViewGroup.MarginLayoutParams layoutParams = (MarginLayoutParams) boxView.getLayoutParams();
        layoutParams.width = boxWidth;
        layoutParams.height = boxHeight;
        layoutParams.leftMargin = index == 0 ? 0 : boxSpacing / 2;
        layoutParams.rightMargin = index == (boxCount - 1) ? 0 : boxSpacing / 2;
        boxView.setLayoutParams(layoutParams);
        boxView.setGravity(Gravity.CENTER);
        boxView.setLines(1);
        boxView.setMaxLines(1);
        boxView.setSingleLine(true);
        boxView.setInputType(InputType.TYPE_NUMBER_VARIATION_PASSWORD | InputType.TYPE_CLASS_NUMBER);
        boxView.setTextSize(TypedValue.COMPLEX_UNIT_PX, boxTextSize);
        boxView.setTextColor(Color.BLACK);
        boxView.setTypeface(Typeface.DEFAULT_BOLD);
        boxView.setBackgroundResource(boxBackground);
        boxView.setCursorVisible(false);
    }

    private void updateBox() {
        if(boxCount > 0) {
            for(int index = 0; index < boxCount; index++) {
                TailInputEditText boxView;
                if(index < getChildCount()) {
                    boxView = getBoxView(index);
                } else {
                    boxView = new TailInputEditText(getContext());
                    addView(boxView, index);
                }
                updateBoxView(boxView, index);
            }
            for(int index = boxCount; index < getChildCount(); index++) {
                removeViewAt(index);
            }
        }
    }

    public void setBoxCount(int boxCount) {
        this.boxCount = Math.max(0, boxCount);
        updateBox();
    }

    public void setBoxWidth(int boxWidth) {
        this.boxWidth = boxWidth;
        updateBox();
    }

    public void setBoxHeight(int boxHeight) {
        this.boxHeight = boxHeight;
        updateBox();
    }

    public void setBoxSpacing(int boxSpacing) {
        this.boxSpacing = boxSpacing;
        updateBox();
    }

    public void setBoxTextSize(float boxTextSize) {
        this.boxTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                boxTextSize, getResources().getDisplayMetrics());
        updateBox();
    }

    public void setOnCodeChangeListener(OnCodeChangeListener listener) {
        this.onCodeChangeListener = listener;
    }

    public void setCode(CharSequence... code) {
        if(code != null) {
            int codeCount = Math.min(code.length, getChildCount());
            for (int index = 0; index < codeCount; index++) {
                setCode(code[index], index);
            }
        }
    }

    public void setCode(CharSequence code, int index) {
        TailInputEditText boxView = getBoxView(index);
        if(boxView != null) {
            String codeText = boxView.getText().toString();
            if(!TextUtils.equals(codeText, code)) {
                boxView.setText(code);
            }
        };
    }

    public CharSequence[] getCode() {
        CharSequence[] codeArray = new CharSequence[getChildCount()];
        for(int index = 0; index < codeArray.length; index++) {
            codeArray[index] = getBoxView(index).getText().toString();
        }
//        return TextUtils.join("", codeArray);
        return codeArray;
    }

    public void setFocusIndex(int index) {
        setFocusIndex(index, true);
    }
    public void setFocusIndex(int index, boolean needEvokeKeyboard) {
        // todo 要解决按速过快导致焦点没有移动到想要的地方还停留在最后一格
        lastFocusIndex = Math.max(0, Math.min(boxCount - 1, index));
        TailInputEditText boxView = getBoxView(lastFocusIndex);
        boxView.postDelayed(boxView::requestFocus, 50);
        if(needEvokeKeyboard) {
            boxView.postDelayed(() -> UIUtil.showKeyboard(boxView.getContext(), boxView), 300);
        }
    }

    private class BoxTextTextWatcher implements TextWatcher, OnKeyListener {

        private final int index;

        private BoxTextTextWatcher(int index) {
            this.index = index;
        }

        @Override
        public void beforeTextChanged(CharSequence text, int start, int count, int after) {}

        @Override
        public void onTextChanged(CharSequence text, int start, int before, int count) {
            setCode(String.valueOf(text), index);
            if(!TextUtils.isEmpty(text)) {
                nextAction();
            }
            if(onCodeChangeListener != null) {
                onCodeChangeListener.onCodeChanged(getCode());
            }
        }

        @Override
        public void afterTextChanged(Editable editable) {
            int length = editable.length();
            if(length > 1) {
                editable.delete(0, length - 1);
            }
        }

        @Override
        public boolean onKey(View view, int keyCode, KeyEvent event) {
            if((view instanceof TailInputEditText) && (event.getAction() == KeyEvent.ACTION_DOWN)) {
                TailInputEditText boxView = (TailInputEditText) view;
                boolean needBack  = TextUtils.isEmpty(boxView.getText());
                switch (event.getKeyCode()) {
                    case KeyEvent.KEYCODE_DEL:
                        backAction(needBack);
                        break;
                }
            }
            return false;
        }

        private void backAction(boolean needBack) {
            if(needBack) {
                int focusIndex = Math.max(0, index - 1);
                TailInputEditText boxView = getBoxView(focusIndex);
                boxView.setText("");
                setFocusIndex(focusIndex, false);
            }
        }

        private void nextAction() {
            setFocusIndex(Math.min(boxCount - 1, index + 1), false);
        }

    }

    public interface OnCodeChangeListener {
        void onCodeChanged(CharSequence[] code);
    }

    @InverseBindingMethods({
            @InverseBindingMethod(type = TextBoxGroupView.class, attribute = "code"),
    })
    public static class TextBoxGroupViewAttrAdapter {
        @BindingAdapter(value = {"onCodeChanged", "codeAttrChanged"}, requireAll = false)
        public static void setOnCodeChangeListener(TextBoxGroupView view, final OnCodeChangeListener listener,
                                                   final InverseBindingListener codeChange) {
            if (codeChange == null) {
                view.setOnCodeChangeListener(listener);
            } else {
                view.setOnCodeChangeListener(code -> {
                    if (listener != null) {
                        listener.onCodeChanged(code);
                    }
                    codeChange.onChange();
                });
            }
        }
    }

}
